﻿using FunctionalCLR.Core;
using FunctionalCLR.Core.Function.Arithmetics;
using RootFinding;

namespace Zove.Runtime.InferencingStrategy {
    public class EquationInferencingStrategy : IInferencingStrategy {
        #region IInferenceStrategy Members

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public int Priority() {
            return 1;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sln"></param>
        public void Infer(Instance sln) {
            bool success = false;
            bool updated = false;

            do {
                updated = false;
                foreach (RuntimeEquation r in sln.Model.Equations) {
                    foreach (RuntimeFactor e in r.Factors) {
                        if (e.Status != RuntimeFactorStatus.Unknown) {
                            continue;
                        }

                        RuntimeRule rr = sln.Model.GetRuntimeRule(r, e);
                        if (rr.Root == null) {
                            success = true;
                            foreach (RuntimeFactor rff in r.Factors) {
                                if (e == rff) {
                                    continue;
                                }

                                if (rff.Status == RuntimeFactorStatus.Unknown) {
                                    success = false;
                                    break;
                                }
                            }
                            if (success) {
                                updated = true;
                                SolvedFactor se = new SolvedFactor();
                                se.Strategy = this;
                                se.Factor = e;
                                se.Rule = rr;

                                this.Evaluate(se);
                                e.Status = RuntimeFactorStatus.Solved;
                                e.SolvedRule = rr;


                                sln.Queue[e.Name] = se;
                            }
                        }
                    }
                }
            }
            while (updated);
        }

        public double Evaluate(SolvedFactor sf) {
            RuntimeEquation r = sf.Rule.Equation;
            SubNode node = new SubNode();
            node.Left = r.Left;
            node.Right = r.Right;

            UnaryFunctionTranslator ef
                = new UnaryFunctionTranslator(
                    ZoveDependencyManager.Instance.FunctionManager, sf.Factor.Name);
            string ne = ef.Translate(node);
            sf.Factor.Value = this.Evaluate(ne);

            return sf.Factor.Value;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public double Evaluate(string expr) {
            IRootFinder rootFinder = new SimpleRootFinder();
            double result = rootFinder.Solve(expr, 0, double.MaxValue);
            return result;
        }

        #endregion
    }
}
